存储IO落盘原理及路径分析

您所在的位置:网站首页 raid io 存储IO落盘原理及路径分析

存储IO落盘原理及路径分析

2023-05-31 15:26| 来源: 网络整理| 查看: 265

作者:吴业亮 博客:wuyeliang.blog.csdn.net

一、IO落盘原理

一个I/O请求,从应用层到底层块设备,路径如下图所示: 在这里插入图片描述

从上图可以看出IO路径是很复杂的。我们将IO路径简化一下: 在这里插入图片描述

一个I/O请求进入block layer之后,可能会经历下面的过程: • Remap: 可能被DM(Device Mapper)或MD(Multiple Device, Software RAID) remap到其它设备

• Split: 可能会因为I/O请求与扇区边界未对齐、或者size太大而被分拆(split)成多个物理I/O

• Merge: 可能会因为与其它I/O请求的物理位置相邻而合并(merge)成一个I/O

• 被IO Scheduler依照调度策略发送给driver

• 被driver提交给硬件,经过HBA、电缆(光纤、网线等)、交换机(SAN或网络)、最后到达存储设备,设备完成IO请求之后再把结果发回。

根据以上步骤对应的时间戳就可以计算出I/O请求在每个阶段所消耗的时间:

Q2Q — time between requests sent to the block layerQ2G – 生成IO请求所消耗的时间,包括remap和split的时间;G2I – IO请求进入IO Scheduler所消耗的时间,包括merge的时间;Q2M — time from a block I/O is queued to the time it gets merged with an existing requestI2D – IO请求在IO Scheduler中等待的时间;D2C – IO请求在driver和硬件上所消耗的时间;Q2C – 整个IO请求所消耗的时间(Q2I + I2D + D2C = Q2C),相当于iostat的await。

用一张图来表示: 在这里插入图片描述

如果I/O性能慢的话,以上指标有助于进一步定位缓慢发生的地方:

D2C可以作为硬件性能的指标;I2D可以作为IO Scheduler性能的指标。

附上event速查表: 在这里插入图片描述

注意以下几点: • Q2Q是表示两个reqeusts之间的间隔;而其他都是表示一个request的各个阶段的间隔。 • 和第2节中的IO的流程图一致,分为两个路径:上面绿色Q->G->I->D表示一个典型request(没有被merge的request)特有的阶段;下面红色为Q->M->D表示一个被merge的request特有的阶段;黄色部分表示公共阶段。 • Q2G一般比较小,若它比较大,说明分配不到struct request,中途经历了S(sleep)阶段。

二、IO路径分析工具blktrace

由于在iostat输出中,只能看到service time + wait time,因为对于评估一个磁盘或者云磁盘而言,service time才是衡量磁盘性能的核心指标和直接指标。很不幸iostat无法提供这个指标,但是blktrace可以。blktrace是一柄神器,很多工具都是基于该神器的:ioprof,seekwatcher,iowatcher,这个工具基本可以满足我们的对块设备请求的所有了解。

1、Blktrace工作原理 blktrace是block层的trace机制,它可以跟踪IO请求的生成、进入队列、发到driver以及完成等事件。blktrace包含3个组件:

• 一个内核模块 • 一个把内核模块跟踪到的IO信息导出到用户态的工具(blktrace命令) • 一些分析和展示IO信息的工具(blkparse, btt命令)

所以广义的blktrace包含这3个部分,狭义的blktrace只是blktrace命令。本文介绍广义的blktrace:包括使用blktrace命令导出IO信息,然后使用blkparse和btt分析展示。blktrace命令通过 debug file system 从内核导出数据: • blktrace从kernel接收数据并通过debug file system的buffer传递到用户态;debug file system默认是/sys/kernel/debug,可以使用blktrace命令的-r选项来覆盖。buffer的大小和数量默认分别是512KiB和4,可以通过-b和-n来覆盖。blktrace运行的的时候,可以看见debug file system里有一个block/{device}(默认是/sys/kernel/debug/block/{device})目录被创建出来,里面有一些trace{cpu}文件。

• blktrace默认地搜集所有trace到的事件,可以使用blktrace命令的-a选项来指定事件。

• blktrace把从内核接收到的数据写到当前目录,文件名为{device}.blktrace.{cpu},内容是二进制数据(对于人来说是不可读的;blkparse用于解析这些二进制数据)。例如blktrace -d /dev/sdc会生成sdc.blktrace.0, sdc.blktrace.1, … sdc.blktrace.N-1个文件,N为cpu个数。也可使用-o选项来自定义{device}部分,这方便和blkparse结合使用:blktrace的-o参数对应blkparse的-i参数。

• blktrace默认地会一直运行,直到被ctrl-C停掉。可以使用-w选项来指定运行时间,单位是秒。 blktrace会区分两类请求:

• 文件系统请求(fs requests):通常是用户态进程产生的,读写disk的特定位置和特定大小的数据。当然,也有可能由内核产生:flush脏页或sync文件系统的元数据(super block或journal block等)。

• SCSI命令(pc requests):blktrace直接把SCSI命令发送到用户态,blkparse可以解析它。

2、Blktrace安装 yum安装方式

yum install blktrace

源码获取(你也可以从源码安装)

git clone git://git.kernel.org/pub/scm/linux/kernel/git/axboe/blktrace.git bt cd bt make make install

可以查看磁盘上的实时信息:

blktrace -d /dev/sdb -o – | blkparse -i –

这个命令会连绵不绝地出现很多输出,当你输入ctrl+C的时候,会停止。 在这里插入图片描述

命令采集信息,待所有信息采集完毕后,统一分析所有采集到的数据。搜集60s信息的命令如下:

blktrace -d /dev/sdb -w 60

在这里插入图片描述

首先blkparse可以将对应不同cpu的多个文件聚合成一个文件:

blkparse -i sdb -d sdb.blktrace.bin

利用btt协助分析统计

#btt -i sdb.blktrace.bin -l sdb.d2c_latency

在这里插入图片描述

注意:

D2C: 表征块设备性能的关键指标Q2C: 客户发起请求到收到响应的时间D2C 平均时间:0.002494923 秒,即2.49毫秒Q2C 平均时间:0.002496605 秒,即2.49毫秒平均下来,D2C 阶段消耗时间占比 99.9326% 上述命令其实还会产生一些.dat文件,可以看到iops信息 在这里插入图片描述

文件解释:

sys_mbps_fs.dat: 本次统计中所有设备吞吐量 sys_iops_fp.dat: 中是本次统计中所有设备的IOPS

blktrace 能够记录下IO所经历的各个步骤: 在这里插入图片描述

我们一起看下blktrace的输出参数解释: 在这里插入图片描述

• 第一个字段:8,0 这个字段是设备号 major device ID和minor device ID。 • 第二个字段:3 表示CPU • 第三个字段:11 序列号 • 第四个字段:0.009507758 Time Stamp是时间偏移 • 第五个字段:PID 本次IO对应的进程ID • 第六个字段:Event,这个字段非常重要,反映了IO进行到了那一步 • 第七个字段:R表示 Read, W是Write,D表示block,B表示Barrier Operation • 第八个字段:223490+56,表示的是起始block number 和 number of blocks,即我们常说的Offset 和 Size • 第九个字段: 进程名

其中第六个字段非常有用:每一个字母都代表了IO请求所经历的某个阶段。

Q – 即将生成IO请求 G – IO请求生成 I – IO请求进入IO Scheduler队列| D – IO请求进入driver| C – IO请求执行完毕

Summary包括CPU和device两个维度: 在这里插入图片描述

其中对于每个CPU或者对于Total: • Writes Queued:trace时间内,Q(queue)的requests个数;

• Writes Requeued:trace时间内,requeue数。requeue可能来自于multi-layer

• Write Dispatches:trace时间内,block layer发送到driver的requests数;

• Write Merges:trace时间内,merge的requests数;

• Writes Completed:trace时间内,完成的requests数;

• Timer unplugs:超时导致的unplug数?

Writes Queued与Writes Requeued之和是trace时间内block layer接收的requests总数(incomming requests)。Write Dispatches和Write Merges之和是trace时间内block layer发出的(到driver)requests数(outgoing requests)。进等于出,所以:

Writes Queued + Writes Requeued = Write Dispatches + Write Merges

Write Dispatches是block layer发送到driver的requests数,但不是所有发送到driver的都完成了,其中有一部分被requeue了,即:

Write Dispatches = Writes Completed + Writes Requeued

我们来分析一个io过程,将一块空白的磁盘格式化挂载一个目录,然后再写入一个文件

在这里插入图片描述 在这里插入图片描述

以上就是一次IO请求的生命周期,从actions看到,分别是QGPIUDC

Q:先产生一个该位置的IO意向插入到io队列,此时并没有真正的请求G:发送一个实际的Io请求给设备P(plugging):插入:即等待即将到来的更多的io请求进入队列,以便系统能进行IO优化,减少执行IO请求时花的时间I:将IO请求进行调度,到这一步请求已经完全成型(formed)好了U (unplugging):拔出,设备决定不再等待其他的IO请求并使得系统必须响应当前IO请求,将该IO请求传给设备驱动器。可以看到,在P和U之间会等待IO,然后进行调度。这里会对IO进行一点优化, 但是程度很低,因为等待的时间很短,是纳秒级别的D :发布刚才送入驱动器的IO请求C:结束IO请求,这里会返回一个执行状态:失败或者成功,在进程号处为0表示执行成功,反之则反 到此一个IO的周期就结束了

案例分析: 案例一: 把/dev/sde(设备号:8,64)整个盘格式化成ext4,挂载到/mnt并使用fio写/mnt/fio-test-file (rw=randwrite ioengine=psync bs=4k fsync=1),同时trace /dev/sde

# blktrace -d /dev/sde -w 60 # blkparse -i sde ...... 8,64 11 2944 33.779658276 90790 Q W 7614344 + 24 [fio] 8,64 11 2945 33.779659493 90790 G W 7614344 + 24 [fio] 8,64 11 2946 33.779660124 90790 P N [fio] 8,64 11 2947 33.779661287 90790 I W 7614344 + 24 [fio] 8,64 11 2948 33.779661791 90790 U N [fio] 1 8,64 11 2949 33.779662409 90790 D W 7614344 + 24 [fio] 8,64 11 2950 33.791580458 0 C W 7614344 + 24 [0] 8,64 11 2951 33.791608964 90790 Q WS 7614368 + 8 [fio] 8,64 11 2952 33.791609940 90790 G WS 7614368 + 8 [fio] 8,64 11 2953 33.791610381 90790 P N [fio] 8,64 11 2954 33.791611867 90790 I WS 7614368 + 8 [fio] 8,64 11 2955 33.791612372 90790 U N [fio] 1 8,64 11 2956 33.791612977 90790 D WS 7614368 + 8 [fio] 8,64 11 2957 33.799933777 0 C WS 7614368 + 8 [0] 8,64 13 5709 33.799963426 90708 Q WS 3905206216 + 8 [jbd2/sde-8] 8,64 13 5710 33.799964662 90708 G WS 3905206216 + 8 [jbd2/sde-8] 8,64 13 5711 33.799965100 90708 P N [jbd2/sde-8] 8,64 13 5712 33.799965942 90708 Q WS 3905206224 + 8 [jbd2/sde-8] 8,64 13 5713 33.799966423 90708 M WS 3905206224 + 8 [jbd2/sde-8] 8,64 13 5714 33.799967148 90708 Q WS 3905206232 + 8 [jbd2/sde-8] ...... CPU8 (sde): Reads Queued: 0, 0KiB Writes Queued: 195, 29,228KiB Read Dispatches: 0, 0KiB Write Dispatches: 170, 29,228KiB Reads Requeued: 0 Writes Requeued: 0 Reads Completed: 0, 0KiB Writes Completed: 170, 29,228KiB Read Merges: 0, 0KiB Write Merges: 25, 100KiB Read depth: 0 Write depth: 20 IO unplugs: 104 Timer unplugs: 0 CPU9 (sde): Reads Queued: 0, 0KiB Writes Queued: 991, 16,156KiB Read Dispatches: 0, 0KiB Write Dispatches: 574, 16,156KiB Reads Requeued: 0 Writes Requeued: 0 Reads Completed: 0, 0KiB Writes Completed: 574, 16,156KiB Read Merges: 0, 0KiB Write Merges: 417, 1,668KiB Read depth: 0 Write depth: 20 IO unplugs: 346 Timer unplugs: 0 ...... Total (sde): Reads Queued: 0, 0KiB Writes Queued: 8,763, 352,192KiB Read Dispatches: 0, 0KiB Write Dispatches: 5,380, 352,192KiB Reads Requeued: 0 Writes Requeued: 0 Reads Completed: 0, 0KiB Writes Completed: 5,380, 352,196KiB Read Merges: 0, 0KiB Write Merges: 3,383, 13,532KiB IO unplugs: 3,092 Timer unplugs: 0 Throughput (R/W): 0KiB/s / 5,872KiB/s Events (sde): 39,850 entries Skips: 0 forward (0 - 0.0%)

我们尝试把一个特定request的事件给找出来(grep一个特定的起始sector号,而不是grep一个序列号):

# blkparse -i sde | grep -w 7614368 8,64 11 2951 33.791608964 90790 Q WS 7614368 + 8 [fio] 8,64 11 2952 33.791609940 90790 G WS 7614368 + 8 [fio] 8,64 11 2954 33.791611867 90790 I WS 7614368 + 8 [fio] 8,64 11 2956 33.791612977 90790 D WS 7614368 + 8 [fio] 8,64 11 2957 33.799933777 0 C WS 7614368 + 8 [0]

这是一个Write+Sync请求,起始sector是7614368,大小是8个secotr(即4k)。它是一个典型的request:经历了Q->G->I->D-C几个阶段,总耗时:33.799933777-33.791608964 = 8324813纳秒 = 8.3毫秒。 Summary部分的Total(sde):

Incomming Requests = Writes Queued + Writes Requeued = 8763 + 0 = 8763 Outgoing Requests = Write Dispatches + Write Merges = 5380 + 3383 = 8763

另外,可以看到各个read指标都为0,因为fio全是写操作且大小和文件系统block对齐。可以对比还是全写操作但bs=5k(不再和文件系统block对齐)的情形,这时候read指标就不为0了,因为不对齐,文件系统需要把部分修改的block读出来,修改然后再写回去。

案例二: 把/dev/sde(设备号:8,64)分区并把/dev/sde2(设备号:8,66)格式化成ext4,挂载到/mnt并使用fio写/mnt/fio-test-file (rw=randwrite ioengine=psync bs=4k fsync=1),同时还是trace /dev/sde:

# blktrace -d /dev/sde -w 60 # blkparse -i sde 8,64 14 1000 21.217022761 96519 D WS 5857775072 + 8 [jbd2/sde2-8] 8,64 14 1001 21.225342198 0 C WS 5857775072 + 8 [0] 8,64 20 1160 21.225404128 98523 A WS 3927579616 + 8


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3